package cpr.context;

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.regex.Pattern;

import cpr.CommandHandler;
import cpr.DefaultCommandHandler;
import cpr.Main;
import cpr.Printer;
import cpr.command.CmdDict;
import cpr.command.CmdHelp;
import cpr.command.CmdOldCommand;
import cpr.command.CmdPrint;
import cpr.command.CmdPrintRule;
import cpr.command.CmdRedo;
import cpr.command.CmdRenumber;
import cpr.command.CmdRenumberCondence;
import cpr.command.CmdRuleSearch;
import cpr.command.CmdScore;
import cpr.command.CmdSetProperty;
import cpr.command.CmdSolutionContext;
import cpr.command.CmdSolutionLoad;
import cpr.command.CmdSolutionPrint;
import cpr.command.CmdSolutionSolve;
import cpr.command.CmdSolutionTest;
import cpr.command.CmdSpectr;
import cpr.command.CmdSplitBy;
import cpr.command.CmdSubcube;
import cpr.command.CmdTestSleep;
import cpr.command.CmdWorkset;
import cpr.command.CmdWorksetAuto;
import cpr.command.CmdWorksetClear;
import cpr.command.CmdWorksetLoad;
import cpr.command.CmdWorksetName;
import cpr.command.CmdWorksetRemove;
import cpr.command.CmdWorksetSave;
import cpr.command.CmdWorksetdAdd;
import cpr.command.db.CmdDbCopyTable;
import cpr.command.db.CmdDbPrintTable;
import cpr.command.test.CmdTstEvalBin;
import cpr.command.test.CmdTstGetAR;
import cpr.data.HLVector;
import cpr.data.SplitCluster;

public class CommandContext {
		
		BufferedWriter console;
		private Context context;
		private Map commandMap;
		public CommandContext(OutputStream os, Properties props) {
			console = new BufferedWriter( new OutputStreamWriter(os));
			startup();
			setConsolePrefix("$");
			context = new Context(props);
			commandMap = new HashMap();
			commandMap.put("$", new ArrayList());
		}
		private Collection registeredCommands;
		
		private Collection registeredControlCommand;
		private String consolePrefix = null;
		public String getConsolePrefix() {
			return consolePrefix;
		}
		
		public Object popCommand(String prefix) {
			List commands = (List)commandMap.get(prefix);
			Object cmd = commands.get(0);
			commands.remove(0);
			return cmd;
		}
		public void pushCommand(String prefix, Object command) {
			List commands = (List)commandMap.get(prefix);
			if (commands==null) {
				commands=  new ArrayList();
				commandMap.put(prefix, commands);
			}
			commands.add(command);
		}
		public boolean hasCommand(String prefix) {
			List commands = (List)commandMap.get(prefix);
			if (commands==null) {
				return false;
			}
			return !commands.isEmpty();
		}
		
		public Context getContext() {
			return context;
		}
		public void setConsolePrefix(String str) {
			consolePrefix = str;
		}
		class CommandControlPushCommand extends DefaultCommandHandler {
			public CommandControlPushCommand() {
				super("push","push a command","","");
			}
			public void execute(String[] params) {
				if (isTaskRunning()) {
					String command = getParameter(params, 0,"");
					String nc = command.substring(4);
					pushCommand("$",nc);
				}
			}
		}
		class CommandControlSetProperty extends DefaultCommandHandler {
			public CommandControlSetProperty() {
				super("set","set property of runnting task","","");
			}
			public void execute(String[] params) {
				if (isTaskRunning()) {
					String key = getParameter(params, 1,"");
					String value = getParameter(params, 2,"");
					taskThreadCH.setProperty(key,value,false);
				}
			}
		}
		class CommandControlStop extends DefaultCommandHandler {
			public CommandControlStop() {
				super("stop","stop running command",null,null);
			}
			public void execute(String[] params) {
				if (isTaskRunning()) {
					taskThread.interrupt();
				}
			}
		}
		class CommandControlPause extends DefaultCommandHandler {
			public CommandControlPause() {
				super("pause","pause running task",null,null);
			}
			public void execute(String[] params) {
				if (isTaskRunning()) {
					taskThread.suspend();
				}
			}
		}
		class CommandControlResume extends DefaultCommandHandler {
			public CommandControlResume() {
				super("resume","resume paused task",null,null);
			}
			public void execute(String[] params) {
				if (isTaskRunning()) {
					taskThread.resume();
				}
			}
		}
		class CommandControlPS extends DefaultCommandHandler {
			public CommandControlPS() {
				super("ps","Process Status shows current process info",null,null);
			}
			public void execute(String[] params) {
				Printer.println("Running: "+taskThreadCH.getName());
				Set set = taskThreadCH.getPropertySet();
				for (Iterator iter = set.iterator(); iter.hasNext();) {
					Object key = iter.next();
					Printer.println(key+"="+taskThreadCH.getProperty(key));
						
				}
				
			}
		}

		private CommandNotFound commandNotFound;
		class CommandNotFound extends DefaultCommandHandler {
			public CommandNotFound() {
				super("");
				// TODO Auto-generated constructor stub
			}
			
			public void execute(String[] params) {
				Printer.println("command not found");
			}
		}
		private void initCommands(Collection collection) {
			for (Iterator iter = collection.iterator(); iter.hasNext();) {
				CommandHandler ch = (CommandHandler) iter.next();
				ch.setCommandContext(this);
			}
		}
		public void startup() {
			registeredCommands= new ArrayList();
			registeredCommands.add(new CmdSplitBy());
			registeredCommands.add(new CmdPrint());
			registeredCommands.add(new CmdPrintRule("rule", CmdPrintRule.TYPE_GLOBAL));
			registeredCommands.add(new CmdPrintRule("sol-rule", CmdPrintRule.TYPE_SOLVE_CONTEXT));
			registeredCommands.add(new CmdRuleSearch());
			registeredCommands.add(new CmdSpectr());
			registeredCommands.add(new CmdWorksetAuto());
			registeredCommands.add(new CmdWorksetdAdd());
			registeredCommands.add(new CmdWorksetClear());
			registeredCommands.add(new CmdWorksetRemove());
			registeredCommands.add(new CmdWorksetSave());
			registeredCommands.add(new CmdWorksetLoad());
			registeredCommands.add(new CmdWorksetName());
			registeredCommands.add(new CmdWorkset());
			registeredCommands.add(new CmdSolutionContext());
			registeredCommands.add(new CmdSolutionSolve());
			registeredCommands.add(new CmdTstEvalBin());
			registeredCommands.add(new CmdTstGetAR());
			
			
			registeredCommands.add(new CmdSolutionLoad());
			registeredCommands.add(new CmdSolutionPrint());
			registeredCommands.add(new CmdSolutionTest());
			registeredCommands.add(new CmdSetProperty());
			registeredCommands.add(new CmdOldCommand());
			registeredCommands.add(new CmdHelp(false));
			registeredCommands.add(new CmdTestSleep());
			registeredCommands.add(new CmdSubcube());
			registeredCommands.add(new CmdScore());
			registeredCommands.add(new CmdRedo());
			registeredCommands.add(new CmdDict());
			
            registeredCommands.add(new CmdRenumber());
            registeredCommands.add(new CmdRenumberCondence());

            
			registeredCommands.add(new CmdDbPrintTable());
			registeredCommands.add(new CmdDbCopyTable());
            
            
            
			initCommands(registeredCommands);
			
			registeredControlCommand = new ArrayList();
			registeredControlCommand.add(new CommandControlStop());
			registeredControlCommand.add(new CommandControlPause());
			registeredControlCommand.add(new CommandControlResume());
			registeredControlCommand.add(new CommandControlPS());
			registeredControlCommand.add(new CommandControlSetProperty());
			registeredControlCommand.add(new CommandControlPushCommand());
			registeredControlCommand.add(new CmdHelp(true));
			initCommands(registeredControlCommand);
			
			commandNotFound = new CommandNotFound();
			commandNotFound.setCommandContext(this);
			
			taskThread = new Thread();
			
		}
		
		
		
//		public String getProperty(String key) {
//			if (key.equals(Main.CPRProperty.PROPERTY_HL_CSV)) {
//				String csv_file  = Main.HL_CSV_FILE;
//				if (Main.DIGITAL) {
//					csv_file  = Main.HL_CSV_FILE+".dig";
//				}
//				return csv_file;
//			}
//			return null;
//		}
		public Writer getConsoleWriter() {
			// TODO Auto-generated method stub
			return console;
		}
		protected void finalize() throws Throwable {
			console.close();
			// TODO Auto-generated method stub
			super.finalize();
			
		}
		public void println(String str) {
		try {
			getConsoleWriter().write(str+"\n");
			getConsoleWriter().flush();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		// TODO Auto-generated method stub
			
		}
		private Pattern spacePattern = Pattern.compile( "\\s" );
		private List history = null;
		
		public Collection getCommandHandlers(boolean ctl) {
			if (ctl) {
				return registeredControlCommand;
			} else {
				return registeredCommands;
			}
			
		}
		public Collection getCommandHandlers() {
			return getCommandHandlers(false);
			
		}
		private CommandHandler getCommandHandler(String command, Collection commandSet) {
			for (Iterator iter = commandSet.iterator(); iter.hasNext();) {
				CommandHandler ch = (CommandHandler) iter.next();
				if (ch.getName().equalsIgnoreCase(command)) {
					return ch;
				}
//				if (params.length<cnn.length) continue;
//				boolean f= true;
//				for (int i = 0; i < cnn.length; i++) {
//					if (!cnn[i].equals(params[i])) {
//						f = false;
//						continue;
//					}
//				}
//				if (f) {
//					return ch;	
//				}
			}
			return commandNotFound;
		}
		
		
		private CommandHandler taskThreadRunnable = null;
		private Thread taskThread = null;
		private CommandHandler taskThreadCH = null;
		private String[] taskThreadParams = null;
		private boolean taskRunning = false;
		
		public boolean isTaskRunning() {
			return taskRunning;
		}
		public void setTaskRunning(boolean bool) {
			taskRunning = bool;
			if (taskRunning) {
				setConsolePrefix("["+taskThreadCH.getName()+"]$");
			} else {
				setConsolePrefix("$");
				Printer.println(getConsolePrefix());
			}
		}
		private CommandHandler getTaskCommandHandler() {
			return taskThreadCH;
		}
		private String[] getTaskParams() {
			return taskThreadParams;
		}
		
		class CommandRunnable implements Runnable {
			public CommandRunnable() {
			}
			public void run() {
				setTaskRunning(true);
				try {
					getTaskCommandHandler().execute(getTaskParams());
				} catch (Exception ex) {
					println("Error: "+ex.getMessage());
					ex.printStackTrace();
				}
				setTaskRunning(false);
			}
		}
		
		private void startNewTask(CommandHandler ch, String[] params ) {
			taskThreadCH = ch;
			taskThreadParams = params;
			taskThread = new Thread(new CommandRunnable());
			taskThread.start();
		}
		public void executeold(String command) {
			System.gc();
			String[] params = spacePattern.split(command);
			CommandHandler ch = getCommandHandler(params[0], registeredCommands);
			try {
				ch.execute(params);
			} catch (Exception ex) {
				println("Error: "+ex.getMessage());
				ex.printStackTrace();
			}
		}
		
		
		public List getHistory() {
			return history;
		}
		
		private void addToHistory(String cmd) {
			if (history==null) {
				history = new ArrayList();
			}
			while (history.size()>25) {
				Object first  = history.get(0);
				history.remove(first);
			}
			history.add(cmd);
		}
		
		public void executeInCurrentThread(String command) {
			String[] params = spacePattern.split(command);
			CommandHandler ch = getCommandHandler(params[0],registeredCommands);
			params[0] = command;
			if (ch.startInSeparateThread()) {
				startNewTask(ch,params);
			} else {
				try {
					ch.execute(params);
				} catch (Exception ex) {
					println("Error: "+ex.getMessage());
					ex.printStackTrace();
				}	
			}
		}
		public void execute(String command) {
			addToHistory(command);
			if (isTaskRunning()) {
				String[] params = spacePattern.split(command);
				CommandHandler ch = getCommandHandler(params[0],registeredControlCommand);
				params[0] = command;
				try {
					ch.execute(params);
				} catch (Exception ex) {
					println("Error: "+ex.getMessage());
					ex.printStackTrace();
				}
			} else {
				executeInCurrentThread(command);
			}
		}
		
	    public boolean deeper(HLVector v1, HLVector v2) {
	    	boolean deeper = true;
	    	for (int i = 0; i < v1.values.length; i++) {
				if (v1.values[i]<v2.values[i]) {
					deeper = false;
					break;
				}
			} 
	    	return deeper;
	    }
	    public void setProperty(String key, String value) {
	    	//CPRProperty.setProperty(key,value);
	    	getContext().setProperty(key, value);
	    }
	    public void performLegacyCommand(String mode) {
	    	Main.perform_mode(mode);
	    }
	    
	    
		
		public SplitCluster getClusterItem(Set clusterSet, int i) {
			SplitCluster res = null;
			if (clusterSet.size()>i) {
				Iterator it = clusterSet.iterator();
				res = (SplitCluster) it.next();
				for (int k =0;k<i;k++) {
					res = (SplitCluster)it.next();
				}
			}
			return res;
		}	
	


}
